(ns example.dlt645
  (:require [packet.core :as p]
            [packet.utils :refer [in?]]
            [packet.types :as t :refer [deftype ubcd u ud]]))

;;提取数据标识的片段
(defn di0 [di] (bit-and di 0xff))
(defn di1 [di] (bit-and (bit-shift-right di 8) 0xff))
(defn di2 [di] (bit-and (bit-shift-right di 16) 0xff))
(defn di3 [di] (bit-and (bit-shift-right di 24) 0xff))
(defn di10 [di] (bit-and di 0xffff))
(defn di32 [di] (bit-and (bit-shift-right di 16) 0xffff))

(deftype voltage (ud 2 0.1))
(deftype current (ud 3 0.001))
(deftype power (ud 3 0.0001))
(deftype energy (ud 4 0.01))
(deftype frequency (ud 2 0.01))
(deftype factor (ud 2 0.001))

;;控制字
(t/deftype control-word
    (flag 
     (b7 as dir "传送方向" with [:down :up])
     (b6 as err "异常应答" with [:normal :abnormal])
     (b5 as more "后续帧标志" with [:finally :to-be-continued])
     (b0-4 as fc "功能码" with {0 :reserved 3 :security 8 :broadcast-timing
                                17 :read-data 18 :read-more-data
                                19 :read-comm-addr 20 :write-data 21 :write-comm-addr
                                22 :frozen 23 :modify-comm-rate 24 :modify-password
                                25 :clear-max-demand 26 :clear-meter 27 :clear-event})))


;;电表的负荷记录块
(t/deftype load-record
    0xa0
  0xa0
  (u 1 "负荷记录长度" length -5)
  (t/datetime "mhDMY" as time "时标")
  (or
    0xaa
    [(voltage as ua "A相电压")
     (voltage as ub "B相电压")
     (voltage as uc "C相电压")
     (current as ia "A相电流")
     (current as ib "B相电流")
     (current as ic "C相电流")
     (frequency as f "频率")
     0xaa])
  (or
    0xaa
    [(power as p "总有功功率") 
     (power as pa "A相有功功率") 
     (power as pb "B相有功功率") 
     (power as pc "C相有功功率") 
     (power as q "总无功功率") 
     (power as qa "A相无功功率") 
     (power as qb "B相无功功率") 
     (power as qc "C相无功功率")
     0xaa])
  (or
    0xaa
    [(factor as pf "总功率因数") 
     (factor as pfa "A相功率因数") 
     (factor as pfb "B相功率因数") 
     (factor as pfc "C相功率因数")
     0xaa])
  (or
    0xaa
    [(energy as e1 "正向有功总电量")
     (energy as e2 "反向有功总电量")
     (energy as eq12 "组合无功1总电量")
     (energy as eq34 "组合无功2总电量")
     0xaa])
  (or
    0xaa
    [(energy as eq1 "第一象限无功总电量")
     (energy as eq2 "第二象限无功总电量")
     (energy as eq3 "第三象限无功总电量")
     (energy as eq4 "第四象限无功总电量")
     0xaa])
  (or
    0xaa
    [(power as px "当前有功需量")
     (power as qx "当前无功需量")
     0xaa])
  (u 1 "校验码" checksum use packet.checksum/cs8)
  0xe5)

;;负荷记录模式字
(t/deftype load-record-mode
    (flag
     (b0 as uif "电压、电流、频率")
     (b1 as pq "有、无功功率")
     (b2 as pf "功率因数")
     (b3 as epq "有、无功总电量")
     (b4 as eq1234 "四象限无功总电量")
     (b5 as demand "当前需量")))

;;冻结数据模式字
(t/deftype frozen-mode
    (flag
     (b0 as zpe "正向有功电能")
     (b1 as fpe "反向有功电能")
     (b2 as q1 "组合无功1电能")
     (b3 as q2 "组合无功2电能")
     (b4 as q1234 "四相限无功电能")
     (b5 as fpx "正向有功最大需量")
     (b6 as fpx "反向有功最大需量")
     (b7 as va "变量")))

(t/deftype di-data
    (break when (and (= (di3 $di) 0x06)
                     (<= (di2 $di) 6)
                     (= (di1 $di) 0)
                     (<= (di0 $di) 2))
                (load-record))
  (case $di
    0x04000901 (load-record-mode "负荷记录模式字")
    0x04000902 (frozen-mode "定时冻结数据模式字")
    0x04000903 (frozen-mode "瞬时冻结数据模式字")
    0x04000904 (frozen-mode "约定冻结数据模式字")
    0x04000905 (frozen-mode "整点冻结数据模式字")
    0x04000906 (frozen-mode "日冻结数据模式字")
    0x04000a01 (t/datetime "mhDM" as start-time "负荷记录起始时间")
    0x04000a02 (ubcd 2 as gap1 "第一类负荷记录间隔时间(分)")
    0x04000a03 (ubcd 2 as gap2 "第二类负荷记录间隔时间(分)")
    0x04000a04 (ubcd 2 as gap3 "第三类负荷记录间隔时间(分)")
    0x04000a05 (ubcd 2 as gap4 "第四类负荷记录间隔时间(分)")
    0x04000a06 (ubcd 2 as gap5 "第五类负荷记录间隔时间(分)")
    0x04000a07 (ubcd 2 as gap6 "第六类负荷记录间隔时间(分)")
    
    0x06100101 (voltage as ua "A相电压")
    0x06100102 (voltage as ub "B相电压")
    0x06100103 (voltage as uc "C相电压")
    0x061001ff (++ 0x06100101 - 0x06100103)
    
    0x06100201 (current as ia "A相电流")
    0x06100202 (current as ib "B相电流")
    0x06100203 (current as ic "C相电流")
    0x061002ff (++ 0x06100201 - 0x06100203)
    
    0x06100300 (power as p "总有功功率")
    0x06100301 (power as pa "A相有功功率")
    0x06100302 (power as pb "B相有功功率")
    0x06100303 (power as pc "C相有功功率")
    0x061003ff (++ 0x06100300 - 0x06100303)
    
    0x06100400 (power as q "总无功功率")
    0x06100401 (power as qa "A相无功功率")
    0x06100402 (power as qb "B相无功功率")
    0x06100403 (power as qc "C相无功功率")
    0x061004ff (++ 0x06100400 - 0x06100403)
    
    0x06100500 (factor as pf "总功率因数")
    0x06100501 (factor as pfa "A相功率因数")
    0x06100502 (factor as pfb "B相功率因数")
    0x06100503 (factor as pfc "C相功率因数")
    0x061005ff (++ 0x06100500 - 0x06100503)
    
    0x06100601 (energy as e1 "正向有功总电量")
    0x06100602 (energy as e2 "反向有功总电量")
    0x06100603 (energy as eq12 "组合无功1总电量")
    0x06100604 (energy as eq34 "组合无功2总电量")
    0x061006ff (++ 0x06100601 - 0x06100604)
    
    0x06100701 (energy as eq1 "第一象限无功电量")
    0x06100702 (energy as eq2 "第二象限无功电量")
    0x06100703 (energy as eq3 "第三象限无功电量")
    0x06100704 (energy as eq4 "第四象限无功电量")
    0x061007ff (++ 0x06100701 - 0x06100704)
    
    0x06100801 (power as px "有功需量")
    0x06100802 (power as qx "无功需量")
    0x061008ff (++ 0x06100801 0x06100802)))

(t/deftype error
    (flag
     (b6 as exceed-fees "费率数超")
     (b5 as exceed-day-segs "日时段数超")
     (b4 as exceed-year-segs "年时区数超")
     (b3 as comm-rate-fixed "通信速率不能更改")
     (b2 as wrong-psw "密码错/未授权")
     (b1 as no-data "无请求数据")
     (b0 as other-err "其他错误")))

(t/deftype body
    (break when (= $err :abnormal) (error))
  (when (in? $fc :security :read-data :read-more-data :write-data)
    (u 4 as di "数据标识"))
  (when (and (= $dir :down) (= $fc :read-data) (= (di3 $di) 0x06))
    (ubcd 1 as block-count "负荷记录块数")
    (when (or (= (di32 $di) 0x0610) (= (di0 $di) 1))
      (t/datetime "mhDMY" as start-time "起始时间")))
  (when (and (= $dir :up) (in? $fc :read-data :read-more-data))
    (if (= (di32 $di) 0x0610)
      [(t/datetime "mhDMY" as start-time "起始时间")
       (while as data (< (+ $= (if (= $fc :read-more-data) 1 0))
                         (+ $data-len 10))
              (di-data))]
      (di-data)))
  (when (= $fc :read-more-data) (u 1 as fseq "帧序号")))

(p/defpacket dlt645 :little-endian
  (skip-until 0x68 (ubcd 6 as addr "地址域") 0x68)
  (control-word)
  (u 1 "数据长度" length-of body)
  (encode + 0x33)
  (body as body)
  (end-of-encode)
  (u 1 "校验码" checksum use packet.checksum/cs8)
  0x16)



(def bs (byte-array (packet.utils/hex-string->bytes "
00 00 68 01 76 34 00 17 22 68 
B2 
6C 
33 33 33 39 
D3 D3 
95 
48 4A 43 38 54 
B9 55 B8 55 B9 55 
33 33 33 33 33 33 33 33 33 
CB 7C 
DD 
33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 
DD 
33 43 33 43 33 43 33 43 
DD 
34 35 33 33 33 33 33 33 3C 34 33 33 38 33 33 33 
DD 
B5 33 33 33 38 33 33 33 33 33 33 33 5A 33 33 33 
DD 
33 33 33 33 33 33 
DD 
0B 
18
67 
25
16 11 22 33")))

;;(p/test-packet dlt645 bs)


